﻿using NLog;
using EpubFactory.Common;
using EpubFactory.Template;
using Markdig;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace EpubFactory.Lib99
{
    public partial class MainForm : Form
    {
        protected static readonly Logger _log = LogManager.GetLogger("mainForm", typeof(MainForm));
        private string coverUrl = null;
        private IEnumerable<IBookFileCreator> creatorList;
        private Receipe receipe;
        public MainForm(Receipe receipe)
        {
            this.receipe = receipe;
            InitializeComponent();
        }
        void LoadUrlHistories()
        {
            var history = UserPreference.Load();
            foreach (var url in history.UrlHistories.Where(g => g.Tag == receipe.Name))
            {
                cbUrl.Items.Add(url.Href);
            }
        }
        void GenerateEPub(Book book)
        {
            var preference = AppPreference.Load();
            var targetName = book.Title;

            var baseDir = Path.Combine(preference.OutputDir, "Books");
            if (!Directory.Exists(baseDir))
            {
                Directory.CreateDirectory(baseDir);
            }
            var epubFilePath = Path.Combine(baseDir, targetName);
            book.GenerateChaptersId();
            if (!string.IsNullOrWhiteSpace(coverUrl))
            {
                book.CoverUrl = coverUrl;
            }

            if (creatorList == null)
            {
                creatorList = BookFileCreatorFactory.GetCreatorList(preference.OutputFormatters);
            }
            foreach (var creator in creatorList)
            {
                creator.Generate(book, epubFilePath);
            }
            ShowMsg("生成成功");
        }
        void LoadBookImages(Book book)
        {
            var fileNames = from dir in Directory.EnumerateFiles(book.EPubId, "*.*", SearchOption.AllDirectories)
                            select dir;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (bgWorker.IsBusy)
            {
                if (bgWorker.WorkerSupportsCancellation)
                {
                    bgWorker.CancelAsync();
                }
                btnStart.Text = "开始";
                return;
            }

            var url = this.cbUrl.Text;

            if (string.IsNullOrWhiteSpace(url))
            {
                ssInfo.Text = "无效的地址";
                return;
            }
            bgWorker.RunWorkerAsync(url);
            btnStart.Text = "取消";
        }
        private void CacheCover(Book book)
        {
            var baseDir = GetBookPath(book.EPubId, book.Host);
            var coverPath = Path.Combine(baseDir, "cover.jpg");
            if (!File.Exists(coverPath))
            {

                //Lib99HttpService.Download(book.CoverUrl, coverPath);
            }

            if (File.Exists(coverPath))
            {
                coverUrl = coverPath;
                book.CoverUrl = coverPath;
                pbCover.Image = ImageUtil.Copy(coverPath);
            }
        }
        void CacheBook(Book book)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Book));
            var path = GetBookPath(book);
            book.CoverUrl = coverUrl;
            using (TextWriter writer = new StreamWriter(Path.Combine(path, book.EPubId + ".xml")))
            {
                serializer.Serialize(writer, book);
                writer.Close();
            }
        }
        Book LoadFromCache(string bookId, string host)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Book));
            var path = GetBookPath(bookId, host);
            Book book = null;
            var xmlPath = Path.Combine(path, bookId + ".xml");
            if (File.Exists(xmlPath))
            {
                using (StreamReader reader = new StreamReader(xmlPath))
                {
                    try
                    {
                        book = (Book)serializer.Deserialize(reader);
                        //ssInfo.Text = "获取暂存...";
                    }
                    catch (InvalidOperationException e)
                    {
                        _log.Error("获取暂存出错", e);
                    }
                }

                if (book == null)
                {
                    File.Delete(xmlPath);
                }
                else
                {
                    foreach (var chapter in book.Chapters)
                    {
                        if (chapter.Children != null)
                        {
                            foreach (var child in chapter.Children)
                            {
                                child.Parent = chapter;
                            }
                        }
                    }
                }
            }
            return book;
        }
        void RenderBook(Book book)
        {
            txtTitle.Text = book.Title;
            txtAuthor.Text = string.Join(",", book.Authors);

            if (!string.IsNullOrWhiteSpace(book.CoverUrl))
            {
                if (book.CoverUrl.StartsWith("http"))
                {
                    pbCover.Load(book.CoverUrl);
                }
                else
                {
                    pbCover.Image = ImageUtil.Copy(book.CoverUrl);
                }
            }


            pbProgress.Value = 0;

            pbProgress.Minimum = 0;
            pbProgress.Maximum = book.FlattedChapters.Count();
            pbProgress.Step = 1;

            ssTotal.Text = pbProgress.Maximum.ToString();

            lvChapterList.Items.Clear();

            lvChapterList.BeginUpdate();
            foreach (var chapter in book.Chapters)
            {
                var item = new ListViewItem(chapter.Title);
                item.SubItems.Add(chapter.Href);
                lvChapterList.Items.Add(item);

                if (chapter.Children != null)
                {
                    foreach (var child in chapter.Children)
                    {
                        item = new ListViewItem(child.Title);
                        item.SubItems.Add(child.Href);
                        lvChapterList.Items.Add(item);
                    }
                }
            }
            lvChapterList.EndUpdate();
        }

        private void CreateFolder(Book book)
        {
            var path = GetBookPath(book);
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            if (!Directory.Exists(Path.Combine(".", "Books")))
            {
                Directory.CreateDirectory(Path.Combine(".", "Books"));
            }
        }
        private string GetBookPath(Book book)
        {
            return GetBookPath(book.EPubId, book.Host);
        }
        private string GetBookPath(string bookId, string host)
        {
            var slug = host.GenerateSlug();
            var basePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            var path = Path.Combine(basePath, slug, bookId);
            return path;
        }


        private void btnClearCache_Click(object sender, EventArgs e)
        {
            var url = cbUrl.Text;
            if (!string.IsNullOrWhiteSpace(url))
            {
                string bookId = ExtractBookId(url);
                string host = ExtractHost(url);
                var path = GetBookPath(bookId, host);
                var xmlPath = Path.Combine(path, bookId + ".xml");
                if (File.Exists(xmlPath))
                {
                    File.Delete(xmlPath);
                }
            }
            MessageBox.Show("清除成功");
        }

        private static string ExtractBookId(string url)
        {
            string bookId = url.Substring(0, url.LastIndexOf('/'));
            bookId = bookId.Substring(bookId.LastIndexOf('/') + 1);
            return bookId;
        }
        private static string ExtractHost(string url)
        {
            var uri = (new Uri(url));
            return string.Format("{0}://{1}", uri.Scheme, uri.Host);
        }


        private void tsMenuItemRetry_Click(object sender, EventArgs e)
        {

        }

        private void btnPublish_Click(object sender, EventArgs e)
        {
            var url = this.cbUrl.Text;

            if (string.IsNullOrWhiteSpace(url))
            {
                ssInfo.Text = "无效的地址";
                return;
            }
            string host = ExtractHost(url);
            Task.Run(() =>
            {
                string bookId = ExtractBookId(url);
                var book = LoadFromCache(bookId, host);
                if (book != null)
                {
                    GenerateEPub(book);
                    CacheBook(book);
                    return;
                }
                else
                {
                    MessageBox.Show("信息获取失败请点击开始");
                }
            });
        }

        private void lvChapterList_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                ListViewHitTestInfo li = lvChapterList.HitTest(e.Location);

                if (lvChapterList.GetItemAt(e.X, e.Y) != null)
                {
                    ctxMenu4Chapters.Show(lvChapterList, e.Location);
                }
            }
        }
        private void ShowInfo(string msg)
        {
            Invoke((MethodInvoker)(() =>
            {
                sslblInfo.Text = msg;
            }));
        }
        private void RunInUI(Action action)
        {
            if (IsHandleCreated)
            {
                Invoke((Action)(delegate ()
                {
                    if (action != null)
                    {
                        action.Invoke();
                    }
                }));
            }
        }
        private void ShowMsg(string msg)
        {
            Invoke((MethodInvoker)(() =>
            {
                MessageBox.Show(msg);
            }));
        }

        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var worker = sender as BackgroundWorker;
            var url = e.Argument.ToString();
            var host = receipe.ExtractHost(url);
            string bookId = receipe.ExtractBookId(url);
            if (string.IsNullOrWhiteSpace(bookId))
            {
                ShowMsg("解析url出错");
                return;
            }
            var book = LoadFromCache(bookId, host);
            if (book != null)
            {
                UserPreference.Load().AddUrl(url, receipe.Name, book.Title);
                Action<Book> renderBook = RenderBook;
                Invoke(renderBook, book);
                GenerateEPub(book);
                return;
            }
            receipe.BookSummaryOk = receipe_BookSummaryCompleted;
            //receipe.BookSummaryCompleted += receipe_BookSummaryCompleted;
            //receipe.ChapterContentLoad += receipe_ChapterContentLoad;
            receipe.ChapterOk = receipe_ChapterContentLoad;

            var cts = new CancellationTokenSource();
            cts.Token.Register(worker.CancelAsync);
            ShowInfo("开始请求...");
            book = receipe.Execute(url, cts);

            CreateFolder(book);
            CacheCover(book);
            UserPreference.Load().AddUrl(url, receipe.Name, book.Title);

            if (e.Cancel)
            {
                ShowInfo("取消...");
                pbProgress.Value = 0;
                return;
            }
            ShowInfo("暂存...");
            CacheBook(book);
            ShowInfo("生成...");
            GenerateEPub(book);
        }

        void receipe_ChapterContentLoad(object sender, ChapterEventArgs e)
        {
            RunInUI(() =>
            {
                pbProgress.PerformStep();
                ssOk.Text = pbProgress.Value.ToString();

            });
        }

        void receipe_BookSummaryCompleted(object sender, BookEventArgs e)
        {
            Action<Book> renderBook = RenderBook;
            Invoke(renderBook, e.Book);
            ShowInfo("下载章节...");
        }
        private void bgWorker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
        {
            pbProgress.PerformStep();
            ssOk.Text = pbProgress.Value.ToString();
            var chapter = e.UserState as Chapter;
            lvChapterList.BeginUpdate();
            if (string.IsNullOrWhiteSpace(chapter.Content)
                && !string.IsNullOrWhiteSpace(chapter.Href)
                && !chapter.Href.Equals("#"))
            {
                lvChapterList.Items[chapter.SortOrder].BackColor = Color.Yellow;
                //ssFail.Text = (Int32.Parse(ssFail.Text) + 1).ToString();
                //Interlocked.Increment(ref completedIterations);
            }
            else
            {
                lvChapterList.Items[chapter.SortOrder].BackColor = Color.Green;
                //ssOk.Text = (Int32.Parse(ssOk.Text) + 1).ToString();
            }
            lvChapterList.Items[chapter.SortOrder].EnsureVisible();

            lvChapterList.EndUpdate();
        }
        private void bgWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == true)
            {
                ssInfo.Text = "已取消";
            }
            else if (e.Error != null)
            {
                ssInfo.Text = "出错: " + e.Error.Message;
            }
            else
            {
                ssInfo.Text = "完成";
            }
            btnStart.Text = "开始";
        }

        private void btnChangeCover_Click(object sender, EventArgs e)
        {
            ofdCover.ShowDialog();
            if (!string.IsNullOrWhiteSpace(ofdCover.FileName))
            {
                pbCover.Image = ImageUtil.Copy(ofdCover.FileName);
                coverUrl = ofdCover.FileName;
            }
        }
        private void btnGenerateCover_Click(object sender, EventArgs e)
        {
            var author = txtAuthor.Text.Trim();
            var title = txtTitle.Text.Trim();
            if (string.IsNullOrWhiteSpace(author) || string.IsNullOrWhiteSpace(title))
            {
                MessageBox.Show("标题和作者不能为空");
                return;
            }
            var url = cbUrl.Text;
            string path = null;
            if (!string.IsNullOrWhiteSpace(url))
            {
                string bookId = ExtractBookId(url);
                string host = ExtractHost(url);
                path = GetBookPath(bookId, host);
            }
            else
            {
                path = Path.Combine("Books", ".");
            }

            using (var image = ImageUtil.Generate(title, author))
            {
                path = Path.Combine(path, "c.jpg");
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                image.Save(path);
                coverUrl = path;
            }

            pbCover.Image = ImageUtil.Copy(path);
        }

        private void btnReadme_Click(object sender, EventArgs e)
        {
            string html = receipe.ReadmeHtml;
            if (string.IsNullOrWhiteSpace(html))
            {
                html = receipe.DefaultReadme();
                html = Markdown.ToHtml(html);
            }
            var form = new ReadmeForm(html);
            form.Show();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            var icon = Bitmap.FromHicon(SystemIcons.Question.Handle);
            var image = new Bitmap(icon, new Size(30, 30));
            this.btnReadme.Image = image;
            btnReadme.Text = string.Empty;
            lvChapterList.FullRowSelect = true;
            LoadUrlHistories();
        }
        IEnumerable<string> GetFilesInAllSubdirectories(string root)
        {
            var di = new DirectoryInfo(root);
            return di.GetDirectories()
            .SelectMany(x => GetFilesInAllSubdirectories(x.FullName))
            .Concat(di.GetFiles().Select(x => x.FullName));
        }
    }


    class ListViewNF : ListView
    {
        public ListViewNF()
        {
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.EnableNotifyMessage, true);
        }

        protected override void OnNotifyMessage(Message m)
        {
            if (m.Msg != 0x14)
            {
                base.OnNotifyMessage(m);
            }
        }
    }
}
